/**
 * 
 */
package cvcg.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.click.control.Form;
import org.apache.click.control.Select;
import org.apache.click.extras.control.PickList;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import cvcg.dao.BaseDAO;
import cvcg.dto.GameSessionVoteDTO;
import cvcg.dto.Top5DTO;
import cvcg.dto.Top5ResultDTO;
import cvcg.dto.VoteResultDTO;
import cvcg.model.Game;
import cvcg.model.GameSession;
import cvcg.model.Player;
import cvcg.model.Role;
import cvcg.model.Score;
import cvcg.model.Vote;

/**
 * @author jaracje
 *
 */
@Transactional(propagation=Propagation.REQUIRED)
public class GameSessionServiceImpl implements GameSessionService {

	private BaseDAO baseDAO;

	/**
	 * @param baseDAO dao
	 */
	public void setBaseDAO(BaseDAO baseDAO) {
		this.baseDAO = baseDAO;
	}

	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#getAllPlayers()
	 */
	@SuppressWarnings("unchecked")	
	public List<Player> getAllPlayers() {
		
		return (List<Player>)baseDAO.getObjects(Player.class);
	}
	
	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#getAllGames()
	 */	
	public List<Game> getAllGames() {
		return (List<Game>) baseDAO.getAllGamesOrderedByName();
	}
	
	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#deletePlayer(java.lang.Long)
	 */
	public void deletePlayer(Long id) 
	{
		baseDAO.removeObject(Player.class, id);
	}
	
	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#getPlayerById(java.lang.Long)
	 */
	public Player getPlayerById(Long id) {
		if (id!=null) 
		{
			return (Player)baseDAO.getObject(Player.class, id);
		} else {
			return null;
		}
	}
	
	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#savePlayer(cvcg.model.Player)
	 */
	@Transactional(propagation=Propagation.REQUIRED)
	public void savePlayer(Long playerId,Form playerForm,PickList ownedGamesPickList,PickList ruleMasteredGamesPickList,PickList rolesPickList) 
	{
		// Pre-Load games to attach
		Map<Long,Game> loadedGamesMap = new HashMap<Long,Game>();
		Map<String,Role> loadedRolesMap = new HashMap<String,Role>();				
		Game game;								
		List<Role> roles = getAllRoles();
		for (Role role : roles) {
			loadedRolesMap.put(role.getRoleName(), role);
		}
				
		Player player = getPlayerById(playerId);    	
    	if (player==null) 
    	{
    		player = new Player();    	
    	}

    	Set<Game> gamesToRemove = new HashSet<Game>();
    	Set<String> ownedGamesPicked = new HashSet<String>();
		ownedGamesPicked.addAll(ownedGamesPickList.getSelectedValues());

    	for (Game gameOwned : player.getOwnedGames()) 
    	{
			if (!ownedGamesPicked.contains(gameOwned.getId().toString())) 
			{
				gamesToRemove.add(gameOwned);
			} else {
				ownedGamesPicked.remove(gameOwned.getId().toString());
			}
		}
    	player.getOwnedGames().removeAll(gamesToRemove);

    	gamesToRemove.clear();
		Set<String> rulesMasteredGamesPicked = new HashSet<String>();		
		rulesMasteredGamesPicked.addAll(ruleMasteredGamesPickList.getSelectedValues());		

    	for (Game gameMastered : player.getRuleMasteredGames()) 
    	{
			if (!rulesMasteredGamesPicked.contains(gameMastered.getId().toString())) 
			{
				gamesToRemove.add(gameMastered);
			} else {
				rulesMasteredGamesPicked.remove(gameMastered.getId().toString());
			}
		}
    	player.getRuleMasteredGames().removeAll(gamesToRemove);
    	
    	Set<String> gamesToLoad = new HashSet<String>();
    	gamesToLoad.addAll(ownedGamesPicked);
    	gamesToLoad.addAll(rulesMasteredGamesPicked);

    	for (String gameId : gamesToLoad) {
			game = (Game)baseDAO.getObject(Game.class, Long.valueOf(gameId));
			loadedGamesMap.put(game.getId(),game);
		}
    	    	
    	for (String ownedGameId : ownedGamesPicked) {
			game = loadedGamesMap.get(Long.valueOf(ownedGameId));
			if (game!=null) 
			{
				player.addOwnedGame(game);
			}
		}
    	
    	for (String ruleMasteredGameId : rulesMasteredGamesPicked) {
			game = loadedGamesMap.get(Long.valueOf(ruleMasteredGameId));
			if (game!=null) 
			{
				player.addRuleMasteredGame(game);
			}
		}    
    	
    	if (rolesPickList!=null) 
    	{
			Set<String> rolesPicked = new HashSet<String>();		
			rolesPicked.addAll(rolesPickList.getSelectedValues());
			Set<Role> rolesToRemove = new HashSet<Role>();
			for(Role role : player.getRoles()) 
			{
				if (!rolesPicked.contains(role.getRoleName())) 
				{
					rolesToRemove.add(role);
				} else {
					rolesPicked.remove(role.getRoleName());	
				}
			}
			player.getRoles().removeAll(rolesToRemove);
			
	    	Role role;
	    	for (String roleName : rolesPicked) 
	    	{
	    		role = loadedRolesMap.get(roleName);
	    		if (role!=null)
	    		{
	    			player.addRole(role);
	    		}
	    	}
    	}

    	playerForm.copyTo(player);
    	
		baseDAO.saveObject(player);		
	}

	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#deleteGame(java.lang.Long)
	 */	
	public void deleteGame(Long id) {
		baseDAO.removeObject(Game.class, id);		
	}
	
	public Game getGameById(Long id) {
		if (id!=null) 
		{
			return (Game)baseDAO.getObject(Game.class, id);
		} else {
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#saveGame(java.lang.Long, org.apache.click.control.Form)
	 */	
	public void saveGame(Long gameId, Form gameForm) {
		Game game = getGameById(gameId);
		if (game==null) 
		{
			game = new Game();
		}
		gameForm.copyTo(game);
		baseDAO.saveObject(game);
	}
	
	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#getAllGameSessions()
	 */
	@SuppressWarnings("unchecked")
	public List<GameSession> getAllGameSessions() {
		return (List<GameSession>) baseDAO.getObjects(GameSession.class);
	}

	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#getGameSessionById(java.lang.Long)
	 */
	public GameSession getGameSessionById(Long gameSessionId) {
		if (gameSessionId!=null) 
		{
			return (GameSession)baseDAO.getObject(GameSession.class, gameSessionId);
		} else {
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#getAllRoles()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Role> getAllRoles() 
	{ 
		return (List<Role>) baseDAO.getObjects(Role.class);
	}

	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#deleteGameSession(java.lang.Long)
	 */	
	public void deleteGameSession(Long id) 
	{

		baseDAO.removeObject(GameSession.class, id);
	}
	
	/* (non-Javadoc)
	 * @see cvcg.service.GameSessionService#saveGame(java.lang.Long, org.apache.click.control.Form)
	 */	
	public void saveGameSession(Long gameSessionId, Form gameSessionForm,PickList playedGamesPickList) 
	{
		GameSession gameSession = getGameSessionById(gameSessionId);
		if (gameSession==null) 
		{
			gameSession = new GameSession();
		}
		gameSessionForm.copyTo(gameSession);
		Long gameId;		
		Game game;
		gameSession.removeAllPlayedGames();
		for (String gameIdString : playedGamesPickList.getSelectedValues()) 
		{
			gameId = Long.valueOf(gameIdString);
			game = this.getGameById(gameId);
			if (game!=null) 
			{
				gameSession.addPlayedGame(game);
			}			
		}
		baseDAO.saveObject(gameSession);
	}

	@Override
	public void toggleGameSessionUser(Long gameSessionId, String playerName) 
	{
		GameSession gameSession = getGameSessionById(gameSessionId);
		if (gameSession!=null) 
		{
			if (!gameSession.isPlayerAttendingSession(playerName)) 
			{
				Player player = baseDAO.getPlayerByName(playerName);
				if (player!=null) 
				{
					gameSession.addAttendingPlayer(player);
				}
			} else {
				gameSession.removeAttendingPlayer(playerName);
			}
			baseDAO.saveObject(gameSession);
		}		
	}

	@Override
	public Player getPlayerByName(String playerName) 
	{
		return baseDAO.getPlayerByName(playerName);
	}
	
	public void addGameSessionVote(Long gameSessionId, Long gameId,String playerName) 
	{
		GameSession gameSession = getGameSessionById(gameSessionId);
		if (gameSession!=null) 
		{
			Vote vote = gameSession.getGameSessionVoteByGameIdAndPlayerName(gameId, playerName);
			if (vote==null) 
			{
				Player player = getPlayerByName(playerName);
				if (player!=null) 
				{
					Game game = getGameById(gameId);
					if (game!=null) 
					{
						vote = new Vote();
						vote.setGame(game);
						vote.setPlayer(player);
						gameSession.addGameSessionVote(vote);
						baseDAO.saveObject(gameSession);					
					}
				}
			}
		}
			
	}
	
	public List<GameSessionVoteDTO> getGameSessionVotes(Long gameSessionId) 
	{
		return baseDAO.getGameSessionVotes(gameSessionId);
	}
	
	public void removeGameSessionVote(Long gameSessionId, Long gameId,String playerName) 
	{
		GameSession gameSession = getGameSessionById(gameSessionId);
		if (gameSession!=null) 
		{
			Vote vote = gameSession.getGameSessionVoteByGameIdAndPlayerName(gameId, playerName);			
			if (vote!=null) 
			{
				gameSession.removeGameSessionVote(vote);
				baseDAO.saveObject(gameSession);
			}						
		}
	}
	
	public Set<Long> getVotedGameIdByGameSessionIdAndPlayerName(Long gameSessionId,String playerName) 
	{
		Set<Long> votedGameIds = new HashSet<Long>();
		GameSession gameSession = getGameSessionById(gameSessionId);
		if (gameSession!=null) 
		{
			votedGameIds.addAll(gameSession.getVotedGameIdByPlayerName(playerName));
		}
		return votedGameIds;
	}
	
	public GameSession getNextGameSession() 
	{
		return baseDAO.getNextGameSession();
	}
	
	public List<GameSession> getPhase1GameSessions() {	
		return baseDAO.getGameSessionsByStatus(GameSession.STATUS_PHASE1);
	}

	public List<GameSession> getPhase2GameSessions() {
		return baseDAO.getGameSessionsByStatus(GameSession.STATUS_PHASE2);
	}

	public List<GameSession> getClosedGameSessions() {
		return baseDAO.getGameSessionsByStatus(GameSession.STATUS_CLOSED);
	}
	
	public void goNextPhase(Long gameSessionId) 
	{
		GameSession gs = getGameSessionById(gameSessionId);
		gs.goNextPhase();
		baseDAO.saveObject(gs);		
	}
	
	public void saveTop5(Long gameSessionId,String playerName,List<Top5DTO> top5List) 
	{
		GameSession gameSession = getGameSessionById(gameSessionId);
		Player player = getPlayerByName(playerName);
		Vote vote;
		Game game;
		gameSession.removeTop5ByPlayer(player.getId());
		for (Top5DTO top5 : top5List) 
		{
			if (top5.getPoints()>0) 
			{
				game = getGameById(top5.getGameId());
				vote = new Vote();
				vote.setPlayer(player);
				vote.setGame(game);
				gameSession.addTopVote(vote);
				vote.setWeight(top5.getPoints());
			}
		}			
		baseDAO.saveObject(gameSession);		
	}
	
	public List<VoteResultDTO> getVoteResultForGameSession(Long gameSessionId) 
	{
		List<VoteResultDTO> resultList = new ArrayList<VoteResultDTO>();
		GameSession gameSession = getGameSessionById(gameSessionId);
		Long gameId;
		VoteResultDTO voteResult;
		if (gameSession!=null) 
		{
			Set<Vote> votes = gameSession.getVotes();
			for (Vote vote : votes) 
			{
				gameId = vote.getGameId();
				voteResult = findVoteResultElement(resultList, gameId);
				if (voteResult==null) 
				{
					voteResult = new VoteResultDTO();
					voteResult.setGameId(gameId);
					voteResult.setGameName(vote.getGameName());		
					resultList.add(voteResult);
				}
				voteResult.addVoter(vote.getPlayerName());
			}
		}
		return resultList;
	}
	
	private VoteResultDTO findVoteResultElement(List<VoteResultDTO> list,Long gameId) 
	{
		if (gameId!=null) 
		{
			for (VoteResultDTO voteResult : list) 
			{
				if(gameId.compareTo(voteResult.getGameId())==0) 
				{
					return voteResult;
				}
			}
		}
		return null;
	}
	
	public List<Top5ResultDTO> getTop5ResultForGameSession(Long gameSessionId) 
	{
		List<Top5ResultDTO> resultList = new ArrayList<Top5ResultDTO>();
		GameSession gameSession = getGameSessionById(gameSessionId);
		Long gameId;
		Top5ResultDTO top5Result;
		if (gameSession!=null) 
		{
			Set<Vote> votes = gameSession.getTopVotes();
			for (Vote vote : votes) 
			{
				gameId = vote.getGameId();
				top5Result = findTop5ResultElement(resultList, gameId);
				if (top5Result==null) 
				{
					top5Result = new Top5ResultDTO();
					top5Result.setGameId(gameId);
					top5Result.setGameName(vote.getGameName());		
					resultList.add(top5Result);
				}
				top5Result.addVoter(vote.getPlayerName(),vote.getWeight());
			}
		}
		return resultList;
	}
	
	private Top5ResultDTO findTop5ResultElement(List<Top5ResultDTO> list,Long gameId) 
	{
		if (gameId!=null) 
		{
			for (Top5ResultDTO top5Result : list) 
			{
				if(gameId.compareTo(top5Result.getGameId())==0) 
				{
					return top5Result;
				}
			}
		}
		return null;
	}

	public List<Score> getScoreByPlayerName(String playerName)
	{
		return baseDAO.getScoreByPlayerName(playerName);
	}
	
	public Score getScoreById(Long id) 
	{
		return baseDAO.getScoreById(id);
	}
	
	@SuppressWarnings("unchecked")
	public List<Score> getAllScores() {
		return (List<Score>) baseDAO.getObjects(Score.class);
	}
	
	public void saveScore(Long scoreId, Form scoreForm,Select playerSelect, Select gameSelect)
	{
		Score score = getScoreById(scoreId);
		if (score==null) 
		{
			score = new Score();
		}
		scoreForm.copyTo(score);
		Game game = getGameById(Long.valueOf(gameSelect.getValue()));
		Player player = getPlayerById(Long.valueOf(playerSelect.getValue()));
		score.setPlayer(player);
		score.setGame(game);
		baseDAO.saveObject(score);
	}
	
	public void deleteScore(Long id) 
	{
		baseDAO.removeObject(Score.class, id);
	}
	
	public List<Game> getAllGamesWithoutScore() 
	{
		return baseDAO.getAllGamesWithoutScore();
	}
}
